﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using dk.Helper.Base;
using dk.Helper.Base.Core;
using dk.Helper.Controls.Properties;

namespace dk.Helper.Controls
{
    public partial class MapForm : Form
    {
        private readonly AddAllyForm addAllyForm;
        private readonly AddPlayerForm addPlayerForm;
        private readonly IAttacksPlanner attacksPlanner;
        private readonly ICurrentVillageInfo currentVillageInfo;
        private readonly IWorld world;
        private readonly IWorldMapFilter worldMapFilter;
        private readonly WorldMapFilterForm worldMapFilterForm;
        private readonly IWorldMapSettings worldMapSettings;
        private readonly IWorldMapper worldMapper;

        /// <summary>
        /// Initializes a new instance of the <see cref="MapForm"/> class.
        /// </summary>
        /// <param name="world">The world.</param>
        /// <param name="attacksPlanner"></param>
        public MapForm(IWorld world, IAttacksPlanner attacksPlanner)
        {
            this.world = world;
            this.attacksPlanner = attacksPlanner;
            addAllyForm = new AddAllyForm(world);
            addPlayerForm = new AddPlayerForm(world);
            worldMapFilter = new WorldMapFilter(world);
            worldMapSettings = new WorldMapSettings(world, worldMapFilter);
            worldMapper = new WorldMapper(world);
            worldMapper.WorldMapSettings = worldMapSettings;
            worldMapFilterForm = new WorldMapFilterForm(worldMapFilter);
            worldMapFilterForm.FormClosed += WorldMapFilterForm_FormClosed;
            InitializeComponent();
            filterControl.FilterButton.Click += FilterButton_Click;
            UpdateMapControls();
            UpdateAlliesTable();
            UpdateEnemiesTable();
            UpdateAllyPlayersTable();
            UpdateEnemyPlayersTable();
            currentVillageInfo = new CurrentVillageInfo(world);
        }

        /// <summary>
        /// Gets or sets a value indicating whether [allow map drawing].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [allow map drawing]; otherwise, <c>false</c>.
        /// </value>
        public bool AllowMapDrawing { get; set; }

        /// <summary>
        /// Updates the map controls.
        /// </summary>
        private void UpdateMapControls()
        {
            nudZoom.Value = worldMapSettings.Zoom;
            nudMapCenterX.Value = worldMapSettings.MapCenterX;
            nudMapCenterY.Value = worldMapSettings.MapCenterY;
        }

        /// <summary>
        /// Handles the Click event of the FilterButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void FilterButton_Click(object sender, EventArgs e)
        {
            worldMapFilterForm.ShowDialog();
        }

        /// <summary>
        /// Handles the Paint event of the MapForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs"/> instance containing the event data.</param>
        private void MapForm_Paint(object sender, PaintEventArgs e)
        {
            DrawMap(e.Graphics);
        }

        /// <summary>
        /// Draws the map.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        public void DrawMap(Graphics graphics)
        {
            if (!AllowMapDrawing) return;
            Bitmap bitmap = worldMapper.CreateMap(ClientRectangle);
            Graphics g = Graphics.FromImage(bitmap);
            graphics.DrawImage(bitmap, 0, 0, ClientRectangle, GraphicsUnit.Pixel);
            g.Dispose();
        }

        /// <summary>
        /// Handles the ValueChanged event of the nudZoom control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Zoom_ValueChanged(object sender, EventArgs e)
        {
            worldMapSettings.Zoom = Convert.ToInt32(nudZoom.Value);
            Invalidate();
        }

        /// <summary>
        /// Handles the Resize event of the MapForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MapForm_Resize(object sender, EventArgs e)
        {
            Invalidate();
        }

        /// <summary>
        /// Handles the ValueChanged event of the nudMapCenterX control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MapCenterX_ValueChanged(object sender, EventArgs e)
        {
            worldMapSettings.MapCenterX = Convert.ToInt32(nudMapCenterX.Value);
            Invalidate();
        }

        /// <summary>
        /// Handles the ValueChanged event of the nudMapCenterY control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MapCenterY_ValueChanged(object sender, EventArgs e)
        {
            worldMapSettings.MapCenterY = Convert.ToInt32(nudMapCenterY.Value);
            Invalidate();
        }

        /// <summary>
        /// Handles the MouseMove event of the tableLayoutPanel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void Map_MouseMove(object sender, MouseEventArgs e)
        {
            currentVillageInfo.Village = worldMapper.GetVillage(e.Location);
            UpdateCurrentVillageInfo();
        }

        /// <summary>
        /// Updates the current village info.
        /// </summary>
        private void UpdateCurrentVillageInfo()
        {
            UpdateVillageInfo();
            UpdatePlayerInfo();
            UpdateAllyInfo();
        }

        /// <summary>
        /// Updates the village info.
        /// </summary>
        private void UpdateVillageInfo()
        {
            lblVillageName.Text = currentVillageInfo.Village != null ? currentVillageInfo.Village.Name : string.Empty;
            lblVillageCoord.Text = currentVillageInfo.Village != null
                                       ? "(" + currentVillageInfo.Village.Coord.X + "|" +
                                         currentVillageInfo.Village.Coord.Y + ")"
                                       : string.Empty;
            lblVillagePoints.Text = currentVillageInfo.Village != null
                                        ? currentVillageInfo.Village.Points.ToString()
                                        : string.Empty;
        }

        /// <summary>
        /// Updates the player info.
        /// </summary>
        private void UpdatePlayerInfo()
        {
            lblPlayerName.Text = currentVillageInfo.Player != null ? currentVillageInfo.Player.Name : string.Empty;
            lblPlayerPoints.Text = currentVillageInfo.Player != null
                                       ? currentVillageInfo.Player.Points.ToString()
                                       : string.Empty;
            lblPlayerVillageCount.Text = currentVillageInfo.Player != null
                                             ? currentVillageInfo.Player.VillageCount.ToString()
                                             : string.Empty;
            lblPlayerAverageVillagePoints.Text = currentVillageInfo.Player != null
                                                     ? currentVillageInfo.Player.AverageVillagePoints.ToString()
                                                     : string.Empty;
        }

        /// <summary>
        /// Updates the ally info.
        /// </summary>
        private void UpdateAllyInfo()
        {
            lblAllyTag.Text = currentVillageInfo.Ally != null ? currentVillageInfo.Ally.Tag : string.Empty;
            lblAllyPoints.Text = currentVillageInfo.Ally != null
                                     ? currentVillageInfo.Ally.Points.ToString()
                                     : string.Empty;
        }

        /// <summary>
        /// Handles the FormClosed event of the WorldMapFilterForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosedEventArgs"/> instance containing the event data.</param>
        private void WorldMapFilterForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            Invalidate();
        }

        /// <summary>
        /// Handles the Click event of the btnAddAlly control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAddAlly_Click(object sender, EventArgs e)
        {
            if (addAllyForm.ShowDialog() == DialogResult.OK)
            {
                IAlly ally = addAllyForm.Ally;
                AddAllianceAlly(ally);
            }
        }

        /// <summary>
        /// Adds the alliance ally.
        /// </summary>
        /// <param name="ally">The ally.</param>
        private void AddAllianceAlly(IAlly ally)
        {
            var brush = new SolidBrush(Color.Blue);
            if (ally == null) return;
            worldMapSettings.Allies.Add(ally);
            worldMapSettings.SetBrush(ally, brush);
            UpdateAlliesTable();
            Invalidate();
        }

        /// <summary>
        /// Updates the allies table.
        /// </summary>
        private void UpdateAlliesTable()
        {
            flowAllies.Controls.Clear();
            foreach (IAlly ally in worldMapSettings.Allies.AlliesByTag.Values)
            {
                var newAllyControl = new AllyControl(ally);
                newAllyControl.Removing += AlliesAlly_Removing;
                flowAllies.Controls.Add(newAllyControl);
            }
        }

        private void AlliesAlly_Removing(object sender, EventArgs e)
        {
            var allyControl = (AllyControl) sender;
            worldMapSettings.Remove(allyControl.Ally);
            worldMapSettings.Save();
            flowAllies.Controls.Remove(allyControl);
            Invalidate();
        }

        /// <summary>
        /// Handles the Click event of the btnAddEnemyAlly control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAddEnemyAlly_Click(object sender, EventArgs e)
        {
            if (addAllyForm.ShowDialog() == DialogResult.OK)
            {
                IAlly ally = addAllyForm.Ally;
                AddEnemyAlly(ally);
            }
        }

        /// <summary>
        /// Adds the enemy ally.
        /// </summary>
        /// <param name="ally">The ally.</param>
        private void AddEnemyAlly(IAlly ally)
        {
            var brush = new SolidBrush(Color.Red);
            if (ally == null) return;
            worldMapSettings.Enemies.Add(ally);
            worldMapSettings.SetBrush(ally, brush);
            UpdateEnemiesTable();
            Invalidate();
        }

        /// <summary>
        /// Updates the enemies table.
        /// </summary>
        private void UpdateEnemiesTable()
        {
            flowEnemies.Controls.Clear();
            foreach (IAlly ally in worldMapSettings.Enemies.AlliesByTag.Values)
            {
                var newAllyControl = new AllyControl(ally);
                newAllyControl.Removing += EnemiesAlly_Removing;
                flowEnemies.Controls.Add(newAllyControl);
            }
        }

        private void EnemiesAlly_Removing(object sender, EventArgs e)
        {
            var allyControl = (AllyControl) sender;
            worldMapSettings.Remove(allyControl.Ally);
            worldMapSettings.Save();
            flowEnemies.Controls.Remove(allyControl);
            Invalidate();
        }

        /// <summary>
        /// Handles the Click event of the btnAddPlayer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAddPlayer_Click(object sender, EventArgs e)
        {
            if (addPlayerForm.ShowDialog() == DialogResult.OK)
            {
                IPlayer player = addPlayerForm.Player;
                AddAllyPlayer(player);
            }
        }

        /// <summary>
        /// Adds the ally player.
        /// </summary>
        /// <param name="player">The player.</param>
        private void AddAllyPlayer(IPlayer player)
        {
            SolidBrush brush = addPlayerForm.SolidBrush;
            if (player == null) return;
            worldMapSettings.AllyPlayers.Add(player);
            worldMapSettings.SetBrush(player, brush);
            UpdateAllyPlayersTable();
            Invalidate();
        }

        /// <summary>
        /// Updates the ally players table.
        /// </summary>
        private void UpdateAllyPlayersTable()
        {
            flowPlayers.Controls.Clear();
            foreach (IPlayer player in worldMapSettings.AllyPlayers.PlayersByName.Values)
            {
                var newPlayerControl = new PlayerControl(player);
                newPlayerControl.Removing += AllyPlayerControl_Removing;
                flowPlayers.Controls.Add(newPlayerControl);
            }
        }

        private void AllyPlayerControl_Removing(object sender, EventArgs e)
        {
            var playerControl = (PlayerControl) sender;
            worldMapSettings.Remove(playerControl.Player);
            worldMapSettings.Save();
            flowPlayers.Controls.Remove(playerControl);
            Invalidate();
        }

        /// <summary>
        /// Handles the Click event of the btnAddEnemyPlayer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAddEnemyPlayer_Click(object sender, EventArgs e)
        {
            if (addPlayerForm.ShowDialog() == DialogResult.OK)
            {
                IPlayer player = addPlayerForm.Player;
                AddEnemyPlayer(player, addPlayerForm.SolidBrush);
            }
        }

        /// <summary>
        /// Adds the enemy player.
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="brush"></param>
        private void AddEnemyPlayer(IPlayer player, SolidBrush brush)
        {
            if (player == null) return;
            worldMapSettings.EnemyPlayers.Add(player);
            worldMapSettings.SetBrush(player, brush);
            UpdateEnemyPlayersTable();
            Invalidate();
        }

        /// <summary>
        /// Updates the enemy players table.
        /// </summary>
        private void UpdateEnemyPlayersTable()
        {
            flowEnemyPlayers.Controls.Clear();
            foreach (IPlayer player in worldMapSettings.EnemyPlayers.PlayersByName.Values)
            {
                var newPlayerControl = new PlayerControl(player);
                newPlayerControl.Removing += EnemyPlayerControl_Removing;
                newPlayerControl.Targeting += EnemyPlayerControl_Targeting;
                flowEnemyPlayers.Controls.Add(newPlayerControl);
            }
        }

        private void EnemyPlayerControl_Removing(object sender, EventArgs e)
        {
            var playerControl = (PlayerControl) sender;
            worldMapSettings.Remove(playerControl.Player);
            worldMapSettings.Save();
            flowEnemyPlayers.Controls.Remove(playerControl);
            Invalidate();
        }

        private void EnemyPlayerControl_Targeting(object sender, EventArgs e)
        {
            if (worldMapSettings.AllyPlayers.IsEmpty)
            {
                MessageBox.Show(Resources.AddAllyPlayerForAttacks, Resources.DkHelper, MessageBoxButtons.OK);
                return;
            }
            var playerControl = (PlayerControl) sender;
            foreach (IPlayer player in worldMapSettings.AllyPlayers.Values)
            {
                attacksPlanner.CreateAttacks(player, playerControl.Player);
            }
        }

        /// <summary>
        /// Handles the FormClosing event of the MapForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void MapForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            worldMapSettings.Save();
        }

        /// <summary>
        /// Handles the Opening event of the contextMenuStrip control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            if (currentVillageInfo.Village == null || currentVillageInfo.Player == null)
                e.Cancel = true;
        }

        /// <summary>
        /// Handles the Click event of the AddAlianceAllyToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddAlianceAllyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddAllianceAlly(currentVillageInfo.Ally);
        }

        /// <summary>
        /// Handles the Click event of the AddEnemyAllyToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddEnemyAllyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddEnemyAlly(currentVillageInfo.Ally);
        }

        /// <summary>
        /// Handles the Click event of the AddAllyPlayerToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddAllyPlayerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddAllyPlayer(currentVillageInfo.Player);
        }

        /// <summary>
        /// Handles the Click event of the AddEnemyPlayerToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddEnemyPlayerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddEnemyPlayer(currentVillageInfo.Player, new SolidBrush(Color.Yellow));
        }
    }
}